Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
C# / VB.NET - [VB.NET] Multithreading e TCP socket
Forum - C# / VB.NET - [VB.NET] Multithreading e TCP socket

Avatar
Question (Normal User)
Rookie


Messaggi: 58
Iscritto: 14/12/2010

Segnala al moderatore
Postato alle 0:27
Sabato, 05/02/2011
Questo topic è stato chiuso dal moderatore

EDIT:ho risolto inserendo un try catch nei due thread di controllo (ho pensato di non poter prevedere quando l'utente chiude i socket) che non fà semplicemente nulla xD
Se avete una soluzione migliore mandatemi un PM o rispondete qua per favore ^^


Ciao a tutti ragazzi,
starei scrivendo un miglioramento di una classe che avevo fatto tempo fà con i TCP socket (TcpClient e TcpListener per intenderci).
Il miglioramento consiste nell'eliminare i numerosi backgroundworker che venivano generati a favore del multithreading, il problema è che ricevo un errore riguardo alla cancellazione di un'instanza nel thread di controllo dati che però dovrebbe ancora esistere  :d

Il sorgente della classe è tutto commentato (in un inglese elementare) e non dovrebbe dare grossi problemi,comunque funziona in questo modo:
- SafeEvent è una procedura che permette di eseguire un RaiseEvent in modo thread safe
- Connect imposta una variabile per indicare lo stato del socket su  connecting poi usa il metodo del TcpClient BeginConnect con callback ad una sub che mette lo status su connected, prende il Networkstream, avvia il thread per controllare lo stato del socket ed esegue un SafeEvent sull'evento Connected
- Listen imposta lo status su listening e avvia un nuovo thread contenente un ciclo che si ferma solo quando il TcpListener riceve una richiesta di connessione, esegue un SafeEvent sull'evento ConnectionRequest e se l'user non ha deciso di rifiutare la connessione accetta il socket,imposta il tcpClient, prende il Networkstream,avvia il thread per controllare lo stato del socket infine fà un SafeEvent sul'evento Connected
- TCheck (il thread per controllare lo stato del socket),controlla se il socket è ancora connesso (come alcuni di voi sapranno la proprietà .connected si riferisce solamente all'ultima operazione di I\O eseguita,di conseguenza ho dato un'occhiata a http://msdn.microsoft.com/it-it/library/system.net.sockets ... se lo è avvia il TData sennò chiama Close() e poi fà il SafeEvent di ConnectionLost
- TData controlla se ci sono dati in arrivo,se ce ne sono solleva in modo safe DataArrival sennò esegue TCheck
- Close() in teoria dovrebbe chiudere tutto ma penso sia proprio questo che mi dà problemi


Questa è la classe (ho inserito dei region per facilitare la lettura,quindi magari conviene usare Visual Studio direttamente, conviene quotare il messaggio e prendere il codice dall'editor):
Codice sorgente - presumibilmente VB.NET

  1. Class MikleSocket 'by VirtualMikle
  2. #Region "Declarations and State property"
  3.     'sockets tcp/ip
  4.     Private tcpClient As Net.Sockets.TcpClient, tcpListener As Net.Sockets.TcpListener, NetStream As Net.Sockets.NetworkStream
  5.     'multi-threading listening, thread for new data ,thread for disconnection checking
  6.     Private TListen As Threading.Thread, TData As Threading.Thread, TCheck As Threading.Thread
  7.     'used to close an output connection request, socket status
  8.     Private ConnectAsync As IAsyncResult, _Condition As Status = Status.Disconnected
  9.     'events
  10.     Public Event Connected As EventHandler
  11.     Public Event ConnectionLost As Eventhandler
  12.     Public Event ConnectionRequest As System.ComponentModel.CancelEventHandler
  13.     Public Event DataArrival(ByVal Data() As Byte)
  14.  
  15.     'socket's statuses
  16.     Public Enum Status
  17.         Connected
  18.         Disconnected
  19.         ConnectionRequest
  20.         Listening
  21.         Connecting
  22.     End Enum
  23.  
  24.     'return the current socket's status
  25.     Public ReadOnly Property Condition As Status
  26.         Get
  27.             Return _Condition
  28.         End Get
  29.     End Property
  30. #End Region
  31.  
  32. #Region "Connect"
  33.     Public Sub BeginConnect(ByVal Host As String, ByVal Port As UInt32)
  34.         tcpClient = New Net.Sockets.TcpClient
  35.         'start the operation on another thread and save the IAsyncResult so we'll be able to kill this request
  36.         _Condition = Status.Connecting
  37.         ConnectAsync = tcpClient.BeginConnect(Host, Port, AddressOf ThreadConnected, Nothing)
  38.     End Sub
  39.  
  40.     Private Sub ThreadConnected()
  41.         'get packets stream
  42.         NetStream = tcpClient.GetStream
  43.         'set the status and destroy the  ConnectAsync
  44.         _Condition = Status.Connected
  45.         ConnectAsync = Nothing
  46.         'start checking status
  47.         TCheck = New Threading.Thread(AddressOf CheckStatus) : TCheck.Start()
  48.         SafeEvent(ConnectedEvent, {Me, New EventArgs})
  49.     End Sub
  50. #End Region
  51.  
  52. #Region "Listen"
  53.     Public Sub BeginListen(ByVal Port As UInt32)
  54.             TListen = New Threading.Thread(AddressOf ThreadListen)
  55.             'get the local ip and set the listener to the specified port
  56.             tcpListener = New Net.Sockets.TcpListener(Net.IPAddress.Parse("127.0.0.1"), Port)
  57.             'start listening and set the socket status
  58.         tcpListener.Start()
  59.             _Condition = Status.Listening
  60.             'start the aynchronous operation
  61.             TListen.Start()
  62.     End Sub
  63.  
  64.     'asynchronous operation
  65.     Private Sub ThreadListen()
  66.         Dim Cancel As New System.ComponentModel.CancelEventArgs
  67.         'just wait until there's a connection request
  68.         Do Until tcpListener.Pending
  69.         Loop
  70.         'raise the connection request event in a thread-safe way
  71.         SafeEvent(ConnectionRequestEvent, {Me, Cancel})
  72.         'check if the connection has not been rejected
  73.         If Not Cancel.Cancel Then
  74.             'accept the connection and get objects
  75.             tcpClient = tcpListener.AcceptTcpClient()
  76.             tcpListener.Stop()
  77.             NetStream = tcpClient.GetStream
  78.             _Condition = Status.Connected
  79.             'start checking status
  80.             TCheck = New Threading.Thread(AddressOf CheckStatus) : TCheck.Start()
  81.             'raise safely the connection completed event
  82.             SafeEvent(ConnectedEvent, {Me, New EventArgs})
  83.         End If
  84.     End Sub
  85. #End Region
  86.  
  87. #Region "Data and CheckStatus"
  88.     Private Sub GetData()
  89.         'check if there's new data
  90.         If tcpClient.Available > 0 Then
  91.             Dim Buffer(tcpClient.Available - 1) As Byte
  92.             'get the new data
  93.             NetStream.Read(Buffer, 0, Buffer.Length)
  94.             'thread safe dataarrival event
  95.             SafeEvent(DataArrivalEvent, {Buffer})
  96.         End If
  97.         'start a thread to see if the socket is still connected
  98.         TCheck = New Threading.Thread(AddressOf CheckStatus) : TCheck.Start()
  99.     End Sub
  100.  
  101.     'write the byte data (send a packet)
  102.     Public Sub SendData(ByVal Data() As Byte)
  103.         'if it not connected then throw the right exception
  104.         If _Condition = Status.Connected Then
  105.             NetStream.Write(Data, 0, Data.Length)
  106.         Else : Throw New Net.Sockets.SocketException(10057)
  107.         End If
  108.     End Sub
  109.  
  110.     'overloads with string data
  111.     Public Sub SendData(ByVal Data As String)
  112.         If _Condition = Status.Connected Then
  113.             Dim Buffer() As Byte = System.Text.UTF8Encoding.UTF8.GetBytes(Data)
  114.             NetStream.Write(Buffer, 0, Buffer.Length)
  115.         Else : Throw New Net.Sockets.SocketException(10057)
  116.         End If
  117.     End Sub
  118.  
  119.     'check if the socket is still connected
  120.     Private Sub CheckStatus()
  121.             If tcpClient.Available = 0 AndAlso tcpClient.Client.Poll(0, Net.Sockets.SelectMode.SelectRead) AndAlso tcpListener Is Nothing Then
  122.                 'close all apart from this thread
  123.                 Close()
  124.                 'thread safe connectionlost event
  125.             SafeEvent(ConnectionLostEvent, {Me, New EventArgs})
  126.             'close this thread
  127.             Exit Sub
  128.             End If
  129.             'begin checking for new data
  130.             TData = New Threading.Thread(AddressOf GetData) : TData.Start()
  131.     End Sub
  132. #End Region
  133.  
  134. #Region "Close and SafeEvent"
  135.     'close all
  136.     Public Sub Close()
  137.         On Error Resume Next
  138.         'abort listening thread
  139.         If TListen IsNot Nothing Then TListen.Abort()
  140.         'end tcpclient connection request
  141.         If ConnectAsync IsNot Nothing Then
  142.             tcpClient.EndConnect(ConnectAsync)
  143.             ConnectAsync = Nothing
  144.         End If
  145.         'abort checking-data thread
  146.         If TData IsNot Nothing Then TData.Abort()
  147.         'close listener
  148.         If tcpListener IsNot Nothing Then tcpListener.Server.Close()
  149.         'close client
  150.         If tcpClient IsNot Nothing Then tcpClient.Close()
  151.         'close networkstream
  152.         If NetStream IsNot Nothing Then NetStream.Close()
  153.         _Condition = Status.Disconnected
  154.     End Sub
  155.  
  156.     'thread safe raiseevent
  157.     Private Sub SafeEvent(ByVal MyEvent As [Delegate], ByVal Parameters() As Object)
  158.         If MyEvent IsNot Nothing Then
  159.             Dim CurObj As Object
  160.             'get all the delegates
  161.             For Each MyDel As [Delegate] In MyEvent.GetInvocationList
  162.                 CurObj = MyDel.Target
  163.                 CurObj.BeginInvoke(MyDel, Parameters)
  164.             Next
  165.         End If
  166.     End Sub
  167. #End Region
  168. End Class





Questo è il mini-progettino che ho fatto in una windows form per testare (richieste una listbox ListBox1 e due buttons Button1 e Button2,scusate se non li ho rinominati ma era solo per testare xD)
Codice sorgente - presumibilmente VB.NET

  1. WithEvents client, server As New MikleSocket
  2.     'usata per non fare uno scambio di pacchetti infinito (vedi client_DataArrival)
  3.     Private yet As Boolean = True
  4.  
  5.     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  6.         server.BeginListen(200)
  7.         client.BeginConnect("localhost", 200)
  8.     End Sub
  9.  
  10.     Private Sub server_Connected(ByVal sender As Object, ByVal e As EventArgs) Handles server.Connected
  11.         Do Until client.Condition = MikleSocket.Status.Connected
  12.             Application.DoEvents()
  13.         Loop
  14.         server.SendData("server->client: who are you?")
  15.     End Sub
  16.  
  17.     Private Sub client_DataArrival(ByVal Data() As Byte) Handles client.DataArrival
  18.         ListBox1.Items.Add(System.Text.UTF8Encoding.UTF8.GetString(Data))
  19.         If yet Then
  20.             yet = False
  21.             client.SendData("client-server: i'm your client")
  22.         End If
  23.     End Sub
  24.  
  25.     Private Sub Server_DataArrival(ByVal Data() As Byte) Handles server.DataArrival
  26.         ListBox1.Items.Add(System.Text.UTF8Encoding.UTF8.GetString(Data))
  27.         server.SendData("server->client: nice to meet you!")
  28.     End Sub
  29.  
  30.     Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
  31.         server.Close()
  32.     End Sub
  33.  
  34.     Private Sub server_ConnectionLost(ByVal sender As Object, ByVal e As EventArgs) Handles client.ConnectionLost
  35.         ListBox1.Items.Add("client has lost the connection")
  36.         yet = True
  37.     End Sub
  38.  
  39.     Private Sub client_ConnectionLost(ByVal sender As Object, ByVal e As EventArgs) Handles server.ConnectionLost
  40.         ListBox1.Items.Add("server has lost the connection")
  41.     End Sub
  42. End Class


Ultima modifica effettuata da Question il 05/02/2011 alle 15:48
PM